Reactでユーザ定義のコンポーネントを使う場合は名前の先頭を大文字としよう
こんにちは、CX事業本部の若槻です。
今回は、前回の記事で実装したmaterial-tableのセレクトリストのスタイルをカスタマイズしようとしたらエラーとなってハマり、Reactでユーザ定義のコンポーネントを使う場合は名前の先頭を大文字としないといけないという教訓を得たので共有します。
実現したかったこと
material-table上でデータのフィルタリングを行うためのセレクトリストのスタイルをカスタマイズして、既定(未フィルター時)の横幅を大きくしたいです。
やってみた
現在のセレクトリストのコンポーネントの定義は次のように何もスタイルは適用していません。
import React from 'react'; import MenuItem from '@material-ui/core/MenuItem'; import FormControl from '@material-ui/core/FormControl'; import Select from '@material-ui/core/Select'; const selectList = (props: { columnDef: any; onFilterChanged: (rowId: string, filterValue: string) => void; items: [string, string][]; }) => { const { columnDef, onFilterChanged, items } = props; const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => { onFilterChanged(columnDef.tableData.id, event.target.value as string); }; return ( <FormControl> <Select onChange={handleChange}> {items.map((item) => ( <MenuItem value={item[0]}>{item[1]}</MenuItem> ))} </Select> </FormControl> ); }; export default selectList;
そこで、次のようにコードを変更しました。Material-UIでスタイルのユーザ定義を可能とするmakeStyles()
を使って、minWidth: 80
を定義し、<FormControl>
コンポーネントに適用して最小幅を80
としています。
ハイライト箇所が変更(行追加)部分です。
import React from 'react'; import { makeStyles } from '@material-ui/core/styles'; import MenuItem from '@material-ui/core/MenuItem'; import FormControl from '@material-ui/core/FormControl'; import Select from '@material-ui/core/Select'; const useStyles = makeStyles(() => ({ formControl: { minWidth: 80, }, })); const selectList = (props: { columnDef: any; onFilterChanged: (rowId: string, filterValue: string) => void; items: [string, string][]; }) => { const { columnDef, onFilterChanged, items } = props; const classes = useStyles(); const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => { onFilterChanged(columnDef.tableData.id, event.target.value as string); }; return ( <FormControl className={classes.formControl}> <Select onChange={handleChange}> {items.map((item) => ( <MenuItem value={item[0]}>{item[1]}</MenuItem> ))} </Select> </FormControl> ); }; export default selectList;
しかし、変更後にReactアプリを実行すると次のようなコンパイルエラーとなりました。
./src/components/atoms/SelectList.tsx Line 19:19: React Hook "useStyles" is called in function "selectList" which is neither a React function component or a custom React Hook function react-hooks/rules-of-hooks Search for the keywords to learn more about each error.
調べてみたところReactドキュメントの次のページにある通り、Reactでユーザ定義のコンポーネントを使う場合は名前の先頭を大文字とする必要があるとのことです。
When an element type starts with a lowercase letter, it refers to a built-in component like
<div>
or<span>
and results in a string'div'
or'span'
passed toReact.createElement
. Types that start with a capital letter like<Foo />
compile toReact.createElement(Foo)
and correspond to a component defined or imported in your JavaScript file.
コンポーネント名がselectList
と小文字で始まっているため、useStyles
が<div>
や <span>
のような組み込みのコンポーネントの中で呼ばれたと判断されたようです。
そこでコンポーネント名をselectList
からSelectList
に変更したところ、
import React from 'react'; import { makeStyles } from '@material-ui/core/styles'; import MenuItem from '@material-ui/core/MenuItem'; import FormControl from '@material-ui/core/FormControl'; import Select from '@material-ui/core/Select'; const useStyles = makeStyles(() => ({ formControl: { minWidth: 80, }, })); const SelectList = (props: { columnDef: any; onFilterChanged: (rowId: string, filterValue: string) => void; items: [string, string][]; }) => { const { columnDef, onFilterChanged, items } = props; const classes = useStyles(); const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => { onFilterChanged(columnDef.tableData.id, event.target.value as string); }; return ( <FormControl className={classes.formControl}> <Select onChange={handleChange}> {items.map((item) => ( <MenuItem value={item[0]}>{item[1]}</MenuItem> ))} </Select> </FormControl> ); }; export default SelectList;
次のようにエラー無くReactアプリを実行でき、セレクトリストの既定の横幅も大きくすることができました。
おわりに
material-tableのセレクトリストのスタイルをカスタマイズしようとしたらエラーとなってハマり、Reactでユーザ定義のコンポーネントを使う場合は名前の先頭を大文字としないといけないという教訓を得たという話でした。
Reactの基本のお作法ではあると思うのですが初心者にとってはハマりどころなのではないでしょうか。
参考
- @material-ui/styles | Material-UI
- JS Bites: React hook is called in a function which is neither a React function or [sic] a custom React Hook
以上